//利用哈希crc10，产生10位哈希结果
//v1.0没有处理冲突
//老化时间改为5120秒
// `include "top_define.v"
//学习和查找分开进行
//学习模块：对mac源地址进行学习
module study        ( 
                      clk,
                      rst_n,
                      
                      mac_sour,
                      port_sour,
                      // himac_broadcast_frm,
                      himac_loopback_on_off,

                      mac_addr_en,

                      busy,
                      //与地址表的接口
                      addr_a,
                      data_a,
                      wren_a,
                      q_as,
                      q_as_en,

                      time_now,
                      studying,
                      studying1,
                      initing,
                      study_fail,

/////plazt 9.1
                      collision_detect_on_off,  //  1 on 0 off
                      collision_port_1,         //  mac 地址冲突的第一个源端口号
                      collision_port_2,         //  mac 地址冲突的第二个源端口号
                      collision_mac_addr_1,     //  mac 地址冲突的源MAC地址的高32位
                      collision_mac_addr_2,     //  mac 地址冲突的源MAC地址的低16位 ， 此寄存器的最低位表示是否有冲突
                      collision_wren   ,         //  冲突信息的写使能

                      hash_sour,
                      hash_en,
                      
                      is_Nstudy_en, //是否需要进行学习
                      
                      init_end      //zhangjy for test 2013.9.9
                     );
                     
input clk;
input rst_n;

input[47:0] mac_sour;
input[3 :0] port_sour;
// input       himac_broadcast_frm  ; //是否为himac广播帧
input       himac_loopback_on_off;

input       mac_addr_en;         //mac地址有效

(*mark_debug = "true"*)output busy;

(*mark_debug = "true"*)output[9:0] addr_a; //mac地址表
//data_a = { 6'd0, sur_mac(48), sour_port(4), time_val, time_now(13) }
(*mark_debug = "true"*)output[71:0] data_a;// mac地址表中的数据
(*mark_debug = "true"*)output       wren_a;

(*mark_debug = "true"*)input[71:0]  q_as;
input       q_as_en;

input[12:0] time_now;
(*mark_debug = "true"*) output      studying;
(*mark_debug = "true"*) output      studying1;
(*mark_debug = "true"*) output reg study_fail;
output         initing;
/////9.1

(*mark_debug = "true"*)input [9:0]hash_sour;
(*mark_debug = "true"*)input      hash_en;

input is_Nstudy_en;
output  init_end;//zhangjy for test 2013.9.9
input                 collision_detect_on_off;
output [3:0]         collision_port_1;
output [3:0]         collision_port_2;
output [31:0]         collision_mac_addr_1;
output [15:0]         collision_mac_addr_2;
output                collision_wren;


reg busy;


reg [9:0]  addr_a;
reg [71:0] data_a;
reg        wren_a;
//FSM1
reg [9:0]  addr_a_1;
reg [71:0] data_a_1;
reg        wren_a_1;
//FSM2
reg [9:0]  addr_a_2;
reg [71:0] data_a_2;
reg        wren_a_2;

reg studying;
reg studying1;
reg studying2;
reg[47:0] mac_sour_reg;
reg[3:0] port_sour_reg;
reg[9:0] hash_sour_reg;

// reg[3:0] step;
// reg[2:0] step2;                                     /////plazt 9.17



reg[2:0] c_state;
reg[2:0] n_state;

reg[2:0] cstate;
reg[2:0] nstate;

// reg     himac_broadcast_frm_reg;

reg     init_end;
reg [9:0] init_addr;
(*mark_debug = "true"*)reg study_done;

reg initing;


reg  [3:0]        collision_port_1;
wire [3:0]        collision_port_2;
wire [31:0]        collision_mac_addr_1;
wire [15:0]        collision_mac_addr_2;
reg                collision_wren;

assign             collision_port_2     = port_sour_reg;
assign             collision_mac_addr_1 = mac_sour_reg[47:16];
assign             collision_mac_addr_2 = mac_sour_reg[15:0];
//temp
// reg study_fail;

always@(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
      mac_sour_reg <= 48'h0;
    else if(mac_addr_en)
      mac_sour_reg <= mac_sour;
    else 
      mac_sour_reg <= mac_sour_reg;
  end
always@(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
      port_sour_reg <= 48'h0;
    else if(mac_addr_en)
      port_sour_reg <= port_sour;
    else 
      port_sour_reg <= port_sour_reg;
  end

//fist_c_state_machine--  sour_mac -> SRAM_memory  
localparam   INIT    = 3'b001;
localparam   IDLE    = 3'b010;
localparam   STUDY1  = 3'b100;
localparam   STUDY2  = 3'b000;
//second_cstate_machine-- SRAM_memory-> study_sucess/update
localparam   FSM2_INIT   = 3'b001;
localparam   UPDATE_IDLE = 3'b010;
localparam   STUDY3      = 3'b100;
localparam   STUDY4      = 3'b000;

// always @ (posedge clk or negedge rst_n)
// begin
//     if(~rst_n)
//         himac_broadcast_frm_reg     <=  1'b0;
//     else if (mac_addr_en == 1'b1)
//         // himac_broadcast_frm_reg     <=  himac_broadcast_frm;
//     else
//         himac_broadcast_frm_reg     <=  himac_broadcast_frm_reg;
// end

always @ (posedge clk or negedge rst_n)begin
    if(~rst_n)
        init_addr       <=  10'd0;
    else if (c_state == INIT)
        init_addr       <=  init_addr + 10'd1;
    else
        init_addr       <=  10'd0;
end

always @ (posedge clk or negedge rst_n)begin
    if(~rst_n)
        init_end        <=  1'b0;
    else if (init_addr == 10'h3ff)  //1024个地址
        init_end        <=  1'b1;
    else
        init_end        <=  init_end;
end

always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
      hash_sour_reg <= 10'd0;
    else if(hash_en)
      hash_sour_reg <= hash_sour;
    else 
      hash_sour_reg <= hash_sour_reg;
end
////////////////////////////////////////////
//FSM1_1           
always @ (posedge clk or negedge rst_n)begin
  if(~rst_n)
    c_state <=  INIT;
  else
    c_state <=  n_state;
end 
//FSM1_2  
always @ ( * )begin
  case(c_state)
    INIT : //将地址表所有表项初始化为0
        if (init_end)
            n_state = IDLE;
        else
            n_state = INIT;
    IDLE :
      if(hash_en && !is_Nstudy_en)
        n_state = STUDY1;
      else
        n_state = IDLE;
    STUDY1:
      n_state = STUDY2;
    STUDY2:
      n_state = IDLE;
    default:
        n_state = IDLE;
  endcase
end
//FSM1_3
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    begin 
      addr_a_1 <= 10'd0;
      data_a_1 <= 72'h0;
      wren_a_1 <= 1'b0;
    end 
    else 
      case(n_state)
      INIT :
      begin
        addr_a_1 <= init_addr;
        data_a_1 <= 72'h0;
        wren_a_1 <= 1'b1;
      end 
      STUDY1:
      begin 
        addr_a_1 <= hash_sour;
        data_a_1 <= 72'h0;
        wren_a_1 <= 1'b0;
      end
      default:
      begin 
        addr_a_1 <= 10'b0;
        data_a_1 <= 72'h0;
        wren_a_1 <= 1'b0;
      end 
      endcase
end
/////////////////////////////////////////////////////////////////
//FSM2_1
always @ (posedge clk or negedge rst_n)begin
  if(~rst_n)
    cstate <=  FSM2_INIT;
  else
    cstate <=  nstate;
end 
//FSM2_2
always @ ( * )begin
  case(cstate)
    FSM2_INIT : //将地址表所有表项初始化为0
        if (init_end)
            nstate = UPDATE_IDLE;
        else
            nstate = FSM2_INIT;
    UPDATE_IDLE :
      if(q_as_en)
        nstate = STUDY3;
      else
        nstate = UPDATE_IDLE;
    STUDY3:
      if(study_fail)
        nstate = UPDATE_IDLE;    
      else if(study_done==1'b1 && !study_fail)
        nstate = STUDY4;
      else
        nstate = STUDY3;
    STUDY4:
      nstate = UPDATE_IDLE;
    default:
        nstate = UPDATE_IDLE;
  endcase
end
//FSM2_3
always@(posedge clk or negedge rst_n)
begin
    if(~rst_n)
    begin 
      addr_a_2 <= 10'd0;
      data_a_2 <= 72'h0;
      wren_a_2 <= 1'b0;
    end 
    else 
      case(nstate) 
      STUDY4:
      begin
          if(!study_fail)
            begin
                addr_a_2 <= hash_sour_reg;
                data_a_2 <= {6'b0,mac_sour_reg,port_sour_reg,1'b1,time_now};
                wren_a_2 <= 1'b1;
            end
          else 
            begin
                addr_a_2 <= 10'b0;
                data_a_2 <= 72'h0;
                wren_a_2 <= 1'b0; 
            end
      end
      default:
      begin 
        addr_a_2 <= 10'b0;
        data_a_2 <= 72'h0;
        wren_a_2 <= 1'b0;
      end 
      endcase
end
always@(posedge clk or negedge rst_n)begin
  if(~rst_n)
    study_fail <= 1'b0;
  else if(n_state == STUDY3)
    if(q_as[65:18]==mac_sour_reg && q_as[13]==1'b1)
      begin
        if(q_as[17:14] != port_sour_reg && (himac_loopback_on_off == 1'b0) && (collision_detect_on_off == 1'b1))
          study_fail <= 1'b1;
        else 
          study_fail <= 1'b0;
      end
    else 
      study_fail <= 1'b0;
  else 
    study_fail <= 1'b0;
end   

always@(posedge clk or negedge rst_n)begin
  if(~rst_n)
    collision_wren <= 1'b0;
  else if(n_state == STUDY3)
    if(q_as[65:18]==mac_sour_reg && q_as[13]==1'b1)
      begin
        if(q_as[17:14] != port_sour_reg && (himac_loopback_on_off == 1'b0) && (collision_detect_on_off == 1'b1))
          collision_wren <= 1'b1;
        else 
          collision_wren <= 1'b0;
      end
    else 
      collision_wren <= 1'b0;
  else 
    collision_wren <= 1'b0;
end 
always@(posedge clk or negedge rst_n)begin
  if(~rst_n)
    collision_port_1 <= 4'b0;
  else if(n_state == STUDY3)
    if(q_as[65:18]==mac_sour_reg && q_as[13]==1'b1)
      begin
        if(q_as[17:14] != port_sour_reg && (himac_loopback_on_off == 1'b0) && (collision_detect_on_off == 1'b1))
          collision_port_1 <= q_as[17:14];
        else 
          collision_port_1 <= 4'b0;
      end
    else 
      collision_port_1 <= 4'b0;
  else 
    collision_port_1 <= 4'b0;
end 

always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    busy <= 1'b0;
    else if(n_state==IDLE /*|| nstate == UPDATE_IDLE*/ || nstate == STUDY4)
    busy <= 1'b0;
    else 
    busy <= 1'b1;
end
//studying为1表示study模块需要控制单播表
//如下情况：1、
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    studying <= 1'b0;
    else if(n_state==STUDY1 || n_state==STUDY2 || n_state==INIT
          || nstate==STUDY3 || nstate==STUDY4  || nstate ==FSM2_INIT )
    studying <= 1'b1;
    else 
    studying <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin
  if(~rst_n)
    study_done <= 1'b0;
  else if(nstate==STUDY3)
    study_done <= 1'b1;
  else 
    study_done <= 1'b0;      
end 
//////////////////////////////////////////////////////////
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    initing <= 1'b0;
    else if(n_state==INIT)
    initing <= 1'b1;
    else 
    initing <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    studying1 <= 1'b0;
    else if(n_state==STUDY1 /*|| n_state==INIT*/)
    studying1 <= 1'b1;
    else 
    studying1 <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    studying2 <= 1'b0;
    else if(nstate==STUDY4)
    studying2 <= 1'b1;
    else 
    studying2 <= 1'b0;
end

always @(*) begin
      case({(initing || studying1),studying2})
        2'b10:   begin
                 addr_a = addr_a_1;
                 data_a = data_a_1;
                 wren_a = wren_a_1;
                 end
        2'b01:   begin
                 addr_a = addr_a_2;
                 data_a = data_a_2;
                 wren_a = wren_a_2;
                 end
        default: begin
                 addr_a = 'b0;
                 data_a = 'b0;
                 wren_a = 'b0;
                 end
      endcase
end
//////////////////////////////////////////////////////////

 endmodule
